<?php
namespace Yls\Core;

use Yls;
use Exception;

/**
 * Yls\Core\Error
 * 系统错误捕捉类
 * 
 * @author keluo <keluo@focrs.com>
 * @copyright 2016 Focrs, Co.,Ltd
 * @package Yls
 * @since 2015-01-24
 */
class Error {
    
    public function __construct() {}
    
    /**
     * 
     * 系统错误处理
     * @param integer $code 错误状态码
     * @param string $message
     * @param string $file
     * @param integer $line
     */
    public static function ErrorHandler($code, $message, $file, $line) {
        $trace = self::GetDebugTrace();
        self::Report(compact('code', 'message', 'file', 'line', 'trace'));
    }
    
    /**
     * 异常错误处理
     * @param Exception $e
     */
    public static function ExceptionHandler(Exception $e) {
        self::Report([
            'code' => $e->getCode(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'message' => $e->getMessage(),
            'trace' =>self::GetDebugTrace()
        ]);
    }
    
    /**
     * 致命错误处理
     */
    public static function FatalHandler() {
        if(!is_null($e = error_get_last()) && in_array($e['type'], [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE])) {
            $messages = explode(PHP_EOL, $e['message']);
            $message = array_shift($messages);
            if($messages) unset($messages[0]);
            self::Report([
                'code' => $e['type'],
                'file' => $e['file'],
                'line' => $e['line'],
                'message' => $message,
                'trace' => implode("\n", $messages)
            ]);
        }
    }
    
    /**
     * 错误报告
     * @param array $data debug信息
     */
    protected static function Report(array $data) {
        if(Yls::Instance()->debug) {
            $msg = self::Translate($data['code']).': '.$data['message'].' ['.$data['file'].':'.$data['line'].']';
            $msg .= $data['trace'] ? "\n".'Trace: '."\n".$data['trace']."\n" : '';
            Yls::Response(false, $msg);
        } else {
            if(in_array($data['code'], array(E_WARNING, E_NOTICE, E_USER_WARNING, E_USER_NOTICE, E_DEPRECATED))) return;
            Yls::Response(false, self::Translate($data['code']).': '.$data['message']);
            // @todo 记录到日志
        }
    }
    
    /**
     * 错误代码翻译
     * @param integer $code
     * @return string
     */
    protected static function Translate($code) {
        $map = [
            E_ERROR => 'E_ERROR',
            E_WARNING => 'E_WARNING',
            E_PARSE => 'E_PARSE',
            E_NOTICE => 'E_NOTICE',
            E_STRICT => 'E_STRICT',
            E_CORE_ERROR => 'E_CORE_ERROR',
            E_CORE_WARNING => 'E_CORE_WARNING',
            E_COMPILE_ERROR => 'E_COMPILE_ERROR',
            E_COMPILE_WARNING => 'E_COMPILE_WARNING',
            E_USER_ERROR => 'E_USER_ERROR',
            E_DEPRECATED => 'E_DEPRECATED',
            E_USER_WARNING => 'E_USER_WARNING',
            E_USER_NOTICE => 'E_USER_NOTICE',
            E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
            0 => 'E_YLS_ERROR'
        ];
        return isset($map[$code]) ? $map[$code] : $code;
    }
    
    /**
     * 获取debug跟踪信息
     */
    protected static function GetDebugTrace() {
        $traces = debug_backtrace();
        $trace = $traces[1];
        $MakeTraceString = function($traces) {
            foreach($traces as $k=>$v) {
                $params = [];
                $args = isset($v['args']) ? $v['args'] : [];
                foreach($args as $arg) {
                    $params[] = is_array($arg) ? '[...]' : (is_object($arg) ? '{...}' : $arg);
                }
                $t = isset($v['class']) ? $v['class'] : '';
                $t .= isset($v['type']) ? $v['type'] : '';
                $t .= isset($v['function']) ? $v['function'].'('.implode(', ', $params).')' : '';
                $t .= isset($v['file']) ? ' ['.$v['file'].(isset($v['line']) ? ':'.$v['line'] : '').']' : '';
                $traces[$k] = $t;   
            }
            return implode("\n", $traces);
        };
        if(0 == strcasecmp($trace['function'], 'ExceptionHandler')) {
            $exception = $trace['args'][0];
            return $MakeTraceString($exception->getTrace());
        } elseif(0 == strcasecmp($trace['function'], 'ErrorHandler')) {
            return $MakeTraceString($traces);
        } elseif($trace['function'] == 'FatalHandler') {
            return '';
        }
    }
}